//#define WII 1
/*---------------------------------------------------------------------------------

	celular automata

v0.01
	draw quads
v0.02
	draw a matrix of quads
v0.03
	center matrix on view 0, 0, 0
v0.04
  really center matrix on view 0, 0, 0, instead of moving camera to center
	on center of matrix as i did on v0.03
v0.05
	big board size and good came position
v0.06
	proper board initialization values
v0.07
	setting up initial hardcoded pattern on the board
v0.08
	added interation control.
v0.09
	animation of simple rules.
	live cells move up if the up space is dead.
v0.10
	better frame animation control
v0.11
	pretty colors
v0.12
	actual game rules.
v0.13
	cubes. and vertex color based on game rules (that will make our ineficient life algorith even worse)
v0.14
	rolled back 0.13
	constants for board size.
	function to translate text into a new board. not working yet
v0.15
	fixed my pointers in textTolife() thanks to drudi
v0.16
	added some random patterns
v0.17
	tried to support for power off and reset on the console
	improved makeFile support for gcube + wii on the same source.
	improved frame depending on destination hardware... still not dinamic
v1.0
	zoom in and out with wiimote buttons + and -
	removed PAD calls on the loop. it was hanging my wii.
	i'm still ignoring the edges of the board on the algo, so random life cames from there :)
v1.01
	zoom with wiimote pitch
v1.02
	roll with wiimote roll
v1.2
	stable controls. most hangs where due to u32 numbers where they could get negative. hope all releases now will be stable.
	started edit mode. for now all it does is stop camera movement.
v1.21
	now we take into account the edges. life game is complete.
v1.22
	input is normalized. it behaves as expected.
v1.23
	fixed vars that are passed to GX functions that expect float and were double.
	less dumb function for drawing the quads
v1.24
	need to make 0.0 explicitly float for GX functions that expect float. failing to do so will give a core dump after calling exit(0), go figure
v1.25
	code organization


	TODO:

	edit mode
	save patterns to SD
	menu to load
	*organize code* break down input at least




---------------------------------------------------sfdf------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <math.h>
#include <gccore.h>
#include <ogcsys.h>
#include <string.h>
#ifdef WII
#include <wiiuse/wpad.h>
#endif
#include <ogc/pad.h>
#include <source/util_gx.h>



#define BOARD_ROWS 64
#define BOARD_COLS 64

#ifdef WII
#define LIFE_TIME 20
#else
#define LIFE_TIME 3
#endif

#define CELL_ALIVE 0
#define CELL_DEAD 1
#define CELL_WILL_DIE 2
#define CELL_WILL_BORN 3

#define CELL_SIZE 0.45f
#define CELL_HALFSIZE 0.45f/2
int getPos(int *x, int *y){
	//out of bounds return false
	if( (*x < 0)
		| (*x >= BOARD_COLS )
		| (*y < 0)
		| (*y >= BOARD_ROWS)
		){
		return false ;
	}
	return (*y * BOARD_COLS) + *x;
}
bool isAlive(int board[], int x, int y){
	int cellPos = getPos(&x, &y);
	if( false == cellPos ) return false;
	 int *cell = &board[cellPos];
	return *cell == CELL_ALIVE || *cell == CELL_WILL_DIE;
}
float getColor( float finalColor, int frame, int finalFrame ){
	return (finalColor/finalFrame)*(frame+1);
}
void drawCell( int cell_state, int frame );
void textToLife( int *board, int initialX, int initialY, char *input ){
	int row = initialY;
	int col = initialX;
	int position, i = 0;
	// loop on the string
	while( input[i] != '\0' ){
		//if we've found a newline char, jump to the row bellow
		if( input[i] == '\t' ){
			row--;
			col = initialX;
		}else{
			// get actual cursor
			position = getPos(&row, &col);
			if( false != position ){
				// mark as live?
				if( input[i] != '.' ){
					board[position] = CELL_ALIVE;
				}
			}
		col++;
		}
		i++;
		//if( i > 255 ){ // some limits
		//	break;
		//}
	}
}

#ifdef WII
// NOTE: aren't those two needed for gamecube?
void _wiimotePowerPressed(s32 chan){
	//I cant find this on any lib :(
	//SYS_ResetSystem( SYS_POWEROFF, 0, 0 );
}
// related to wpad input document what this does exactly. just a event counter?
int evctr = 0;
void countevs(int chan, const WPADData *data) {
   evctr++;
}
#endif

//---------------------------------------------------------------------------------
int main( int argc, char **argv ){
//---------------------------------------------------------------------------------

	VIDEO_Init();
	//input
	//PAD_Init();
	//PADStatus pads[4];
#define INPUT_VIEW 0
#define INPUT_EDIT 1
#define INPUT_MENU 3
	int inputMode = INPUT_VIEW;
#ifdef WII
	WPAD_Init();
	WPAD_SetDataFormat(0, WPAD_FMT_BTNS_ACC_IR);
	//WPAD_SetVRes(0, rmode->fbWidth, rmode->xfbHeight);
	//I cant find this function anywhere :(
	//WPAD_SetPowerButtonCallback(_wiimotePowerPressed);
	SYS_SetResetCallback(reload);
	SYS_SetPowerCallback(shutdown);
	int WPAD_res;
	WPADData *WPAD_d;
	u32 type; //what's this about?
	gforce_t gforce;
	//OFF ir_t  ir;
#endif

	initVideo();

	// setup the vertex descriptor
	// tells the flipper to expect direct data
	GX_ClearVtxDesc();
	GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
 	GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);

	// setup the vertex attribute table
	// describes the data
	// args: vat location 0-7, type of data, data format, size, scale
	// so for ex. in the first call we are sending position data with
	// 3 values X,Y,Z of size F32. scale sets the number of fractional
	// bits for non float data.
	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGB8, 0);

	GX_SetNumChans(1); GX_SetNumTexGens(0);
	GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORDNULL, GX_TEXMAP_NULL, GX_COLOR0A0);
	GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR);





	//-----------------------------INICIO-------------------------
	

	// camera move
	float camera_z = -20.0;
	float camera_z_accel = 0.0;
	float camera_roll = 0.0; //angle
	float camera_roll_accel = 0.0;


	bool doStep = false;
	// configure board
	int board[ BOARD_ROWS * BOARD_COLS ];
	int board_index = 0;
	for( board_index = 0; board_index < BOARD_ROWS*BOARD_COLS; board_index++){
		board[board_index] = CELL_DEAD;
		// TODO: need to create a structure instead of bool.
		// it must hold the Fate of the the cell in the next interation.
		// or it can be tinyint and add a new CELL_WILL_DIE, CELL_WILL_BORN, CELL_WILL_REMAIN constants
	}

// draw initial board
char *glider = "\
	.x.\
	..x\
	xxx\0";
char *puff = "\
	*\
	*...*\
	*.*\
	*..\
	*.*\
	*.*\
	*.*\
	*.*\
	*..\
	*.*\
	*...*\
	*.*\
	*..\
	*.*\
	*.*\
	*..\
	*.*\
	*...*\
	*.*\
	*..\
	*.*\
	*.*\
	*..\
	*.*\
	*...*\
	*.*\
	*..\
	*.*\
	*.*\
	*.*\
	*.*\
	*..\
	*.*\
	*...*\
	*.*\
	*\0";
char *quilt = "\
	.............**....**....**....**\
	............*..*...**...*..*...**\
	............*..*........*..*\
	.........**.****.******.****.******\
	.........*.*....*......*....*......*\
	...........*..*..*..*..*..*..*..*..*\
	...........***.**..***.***.**..***.**\
	.........**..**..***.**..**..***.**..*\
	........*...*...*...*...*...*...*...*.*\
	...**..*.*...*...*...*...*...*...*...*.*\
	...*...***.***.***.***.***.***.***.**..***\
	....***..**.***.***.***.***.***.***.***...*\
	.***....*...*...*...*...*...*...*...*...*.*\
	*..*.*...*...*...*...*...*...*...*...*..*.*.**\
	*..*.*.***.***.***.***.***.***.***.***..*.*.**\
	.***..*..**.***.***.***.***.***.***.*.*...*\
	....*.*.*...*...*...*...*...*...*...*.*.*.*\
	...*.*.*.*...*...*...*...*...*...*...*.*.*\
	...*...*.*.***.***.***.***.***.***.**..*..***\
	**.*.*..***.***.***.***.***.***.***.***.*.*..*\
	**.*.*..*...*...*...*...*...*...*...*...*.*..*\
	...*.*...*...*...*...*...*...*...*...*....***\
	...*...***.***.***.***.***.***.***.**..***\
	....***..**.***.***.***.***.***.***.***...*\
	.***....*...*...*...*...*...*...*...*...*.*\
	*..*.*...*...*...*...*...*...*...*...*..*.*.**\
	*..*.*.***.***.***.***.***.***.***.***..*.*.**\
	.***..*..**.***.***.***.***.***.***.*.*...*\
	....*.*.*...*...*...*...*...*...*...*.*.*.*\
	...*.*.*.*...*...*...*...*...*...*...*.*.*\
	...*...*.*.***.***.***.***.***.***.**..*..***\
	**.*.*..***.***.***.***.***.***.***.***.*.*..*\
	**.*.*..*...*...*...*...*...*...*...*...*.*..*\
	...*.*...*...*...*...*...*...*...*...*....***\
	...*...***.***.***.***.***.***.***.**..***\
	....***..**.***.***.***.***.***.***.***...*\
	......*.*...*...*...*...*...*...*...*.*..**\
	.......*.*...*...*...*...*...*...*...*\
	........*..**.***..**..**.***..**..**\
	.........**.***..**.***.***..**.***\
	..........*..*..*..*..*..*..*..*..*\
	..........*......*....*......*....*.*\
	...........******.****.******.****.**\
	..................*..*........*..*\
	.............**...*..*...**...*..*\
	.............**....**....**....**\0";

//TODO: add other patterns
switch( 2 ){
	case 1:
		textToLife( board, 0, BOARD_ROWS, glider);
		break;
	case 3:
		textToLife( board, 10, BOARD_ROWS-1, puff);
		break;
	case 2:
		textToLife( board, 10, BOARD_ROWS-10, quilt);
		break;
	default:
		// random
		for( board_index = 0; board_index < BOARD_ROWS*BOARD_COLS; board_index++){
			board[board_index] = rand()%2;
		}
}


	int frame = 1;
	int i, j; //TODO: better names for those. these are rols/cols for the array
	float x, y; // these are x,y for the board
	bool prepStep = false;
	// Timing Variables
	//clock_t time_now, movement_timer = 0;
	// we're not read yet. http://nehe.gamedev.net/data/articles/article.asp?article=17
   while(!doreload && !dooff) { /////////////////////////////////////////////////////////  MAIN LOOP ///
#if WII
		WPAD_ReadPending(WPAD_CHAN_ALL, countevs);
		WPAD_res = WPAD_Probe(0, &type);
		if( WPAD_res == WPAD_ERR_NONE ){
			WPAD_d = WPAD_Data(0);
			// [HOME]
			if( WPAD_d->btns_h & WPAD_BUTTON_HOME ) exit(0);
			// [DOWN]
			if( WPAD_d->btns_h & ( WPAD_BUTTON_MINUS | WPAD_BUTTON_LEFT) ){
				inputMode = INPUT_EDIT;
				//TODO: edit mode
			}
			if( WPAD_d->btns_h & ( WPAD_BUTTON_PLUS | WPAD_BUTTON_RIGHT) ){
				inputMode = INPUT_VIEW;
			}
			if( inputMode == INPUT_VIEW ){
				// ROLL/PITCH (both are an angle) 
				WPAD_GForce(0, &gforce);
				// pitch
				if( camera_z_accel > 0 ) camera_z_accel -= 0.001; // deacelerates..
				if( camera_z_accel < 0 ) camera_z_accel += 0.001; // ..
				//roll
				if( camera_roll_accel > 0 ) camera_roll_accel -= 0.001; // deacelerates..
				if( camera_roll_accel < 0 ) camera_roll_accel += 0.001; // ..
				if( camera_z > 0 ){
					camera_z_accel += ((float)gforce.y)* -0.006;
					camera_roll_accel += ((float)gforce.x) * 0.003;
				}else{
					camera_z_accel += ((float)gforce.y)* 0.006;
					camera_roll_accel += ((float)gforce.x) * -0.003;
				}
			}else{
				//first, stop movement
				camera_roll_accel = 0;
				camera_z_accel = 0;
			}
		}
#endif
		//ZOOM
		if( camera_z_accel >  2.0 ) camera_z_accel =  2.0;
		if( camera_z_accel < -2.0 ) camera_z_accel = -2.0;
		camera_z += camera_z_accel;
		if( camera_z > 50.0F ) camera_z = 50.0F;
		if( camera_z < -50.0F ) camera_z = -50.0F;
		//ROLL
		// limit degress of aceleration
		if( camera_roll_accel >  1.0 ) camera_roll_accel =  1.0;
		if( camera_roll_accel < -1.0 ) camera_roll_accel = -1.0;
		camera_roll += camera_roll_accel;
		//?? camera_roll = (camera_roll % 360);


		frame++;
		prepStep = (frame % LIFE_TIME)==0?true:false;
		doStep = (frame % LIFE_TIME)==LIFE_TIME/2?true:false;
		// do this before drawing
		GX_SetViewport(0,0,rmode->fbWidth,rmode->efbHeight,0,1);
		//Camera
		Vector cam = {0.0F, 0.0F, camera_z},
					 up = {(float)cos(camera_roll), (float)sin(camera_roll), 0.0F},
					 look = {0.0F, 0.0F, 0.0F};
		guLookAt(view, &cam, &up, &look);
		// draw board
		for( i=0; i < BOARD_ROWS; i++ ){
			y = 0.5f+i - BOARD_ROWS*0.5; // have no clue why i need the +0.5 there
			for( j=0; j < BOARD_COLS; j++ ){
				x =0.5f+ j - BOARD_COLS*0.5;
				int cellPos = BOARD_COLS*i+j;
				int *cell = &board[cellPos];
				// do a next evolution step?
				if( prepStep == true ){
					int neighbours = 0;
					neighbours =
						(isAlive(board, j  , (i+1) )?1:0) + // up
						(isAlive(board, j  , (i-1) )?1:0) + // down
						(isAlive(board, j-1, i     )?1:0) +   // left
						(isAlive(board, j+1, i     )?1:0) +   // right
						(isAlive(board, j-1, (i+1) )?1:0) +//up left
						(isAlive(board, j+1, (i+1) )?1:0) +//up right
						(isAlive(board, j-1, (i-1) )?1:0) +//down left
						(isAlive(board, j+1, (i-1) )?1:0) ;//down right
					if( isAlive(board, j, i) && ( neighbours > 3 || neighbours < 2 )  ){
							*cell = CELL_WILL_DIE;
					}else if( ! isAlive(board, j, i) && neighbours == 3 ){
							*cell = CELL_WILL_BORN;
					}

				}else if( doStep ){
					// draw next generation on board
					if( *cell == CELL_WILL_DIE ){
						*cell = CELL_DEAD;
					}else if( *cell == CELL_WILL_BORN ){
						*cell = CELL_ALIVE;
					}
				}
				//DRAW
					// glTranslatef()
					guMtxIdentity(model);
					//guMtxRotAxisDeg(model, &Yaxis, rtri);
					guMtxTransApply(model, model, x, y, 0.0f);
					guMtxConcat(view,model,modelview);
					// load the modelview matrix into matrix memory
					GX_LoadPosMtxImm(modelview, GX_PNMTX0);
					// /glTranslatef()
					drawCell( board[ BOARD_COLS*i + j ], frame%LIFE_TIME );
				// /DRAW
			}
		}
		//if( doStep && cellsAlive < 1 ){ exit(0); }

		// do this stuff after drawing
		GX_DrawDone();
		fb ^= 1;		// flip framebuffer
		GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
		GX_SetColorUpdate(GX_TRUE);
		GX_CopyDisp(frameBuffer[fb],GX_TRUE);
		VIDEO_SetNextFramebuffer(frameBuffer[fb]);
		VIDEO_Flush();
		//probably only needed in CRT?
		VIDEO_WaitVSync();
	}
	if(doreload) SYS_ResetSystem( SYS_RETURNTOMENU, 0, 0 );
	if(dooff) SYS_ResetSystem(SYS_POWEROFF,0,0); // seems to do nothing.
	return 0;
}
void drawCell( int cell_state, int frame ){
	float r = 0.0;
	float g = 0.0;
	float b = 0.0;
	float r1 = 0.0; // fancy higlighted corner
	float g1 = 0.0; // fancy higlighted corner
	float b1 = 0.0; // fancy higlighted corner
	if( cell_state == CELL_ALIVE ){
		g = 0.5;
		g1 = 0.4;
	}else if( cell_state == CELL_DEAD ){
		g = 0.1;
	}else if( cell_state == CELL_WILL_BORN ){
		g = getColor( 0.5, frame, LIFE_TIME );
		g1 = g+0.06;
	}else if( cell_state == CELL_WILL_DIE ){
		g = 0.5 - getColor( 0.5, frame, LIFE_TIME );
		g1 = g;
	}else{
		r = 1; r1 = 1;
		b = 0.1; b1 = 0.1;
	}
	GX_Begin(GX_QUADS, GX_VTXFMT0, 24);			// Draw A Quad
		// observer front
		GX_Position3f32(-CELL_SIZE, CELL_SIZE, 0.0f);	// Top Left
		GX_Color3f32(r1, g1, b1);
		GX_Position3f32( CELL_SIZE, CELL_SIZE, 0.0f);		// Top Right
		GX_Color3f32(r, g, b);
		GX_Position3f32( CELL_SIZE,-CELL_SIZE, 0.0f);	// Bottom Right
		GX_Color3f32(r, g, b);
		GX_Position3f32(-CELL_SIZE,-CELL_SIZE, 0.0f);	// Bottom Left
		GX_Color3f32(r, g, b);
		//top
		GX_Position3f32(-CELL_SIZE, CELL_SIZE, CELL_SIZE);	// Top Left
		GX_Color3f32(r1, g1, b1);
		GX_Position3f32( CELL_SIZE, CELL_SIZE, CELL_SIZE);		// Top Right
		GX_Color3f32(r, g, b);
		GX_Position3f32( CELL_SIZE, CELL_SIZE, 0.0f);	// Bottom Right
		GX_Color3f32(r, g, b);
		GX_Position3f32(-CELL_SIZE, CELL_SIZE, 0.0f);	// Bottom Left
		GX_Color3f32(r, g, b);
		//bottom
		GX_Position3f32(-CELL_SIZE, -CELL_SIZE, 0.0f);	// Top Left
		GX_Color3f32(r1, g1, b1);
		GX_Position3f32( CELL_SIZE, -CELL_SIZE, 0.0f);		// Top Right
		GX_Color3f32(r, g, b);
		GX_Position3f32( CELL_SIZE, -CELL_SIZE, CELL_SIZE);	// Bottom Right
		GX_Color3f32(r, g, b);
		GX_Position3f32(-CELL_SIZE, -CELL_SIZE, CELL_SIZE);	// Bottom Left
		GX_Color3f32(r, g, b);
		// back invertido
		GX_Position3f32(-CELL_SIZE, CELL_SIZE, 0.0f);	// Top Left
		GX_Color3f32(r1, g1, b1);
		GX_Position3f32( CELL_SIZE, CELL_SIZE, 0.0f);		// Top Right
		GX_Color3f32(r, g, b);
		GX_Position3f32( CELL_SIZE,-CELL_SIZE, 0.0f);	// Bottom Right
		GX_Color3f32(r, g, b);
		GX_Position3f32(-CELL_SIZE,-CELL_SIZE, 0.0f);	// Bottom Left
		GX_Color3f32(r, g, b);
		// observer right
		GX_Position3f32( CELL_SIZE, CELL_SIZE, 0.0f);	// Top Left
		GX_Color3f32(r1, g1, b1);
		GX_Position3f32( CELL_SIZE, CELL_SIZE, CELL_SIZE);		// Top Right
		GX_Color3f32(r, g, b);
		GX_Position3f32( CELL_SIZE,-CELL_SIZE, CELL_SIZE);	// Bottom Right
		GX_Color3f32(r, g, b);
		GX_Position3f32( CELL_SIZE,-CELL_SIZE, 0.0f);	// Bottom Left
		GX_Color3f32(r, g, b);
		// observer left
		GX_Position3f32(-CELL_SIZE, CELL_SIZE, CELL_SIZE);	// Top Left
		GX_Color3f32(r1, g1, b1);
		GX_Position3f32(-CELL_SIZE, CELL_SIZE, 0.0f);		// Top Right
		GX_Color3f32(r, g, b);
		GX_Position3f32(-CELL_SIZE,-CELL_SIZE, 0.0f);	// Bottom Right
		GX_Color3f32(r, g, b);
		GX_Position3f32(-CELL_SIZE,-CELL_SIZE, CELL_SIZE);	// Bottom Left
		GX_Color3f32(r, g, b);
	GX_End();
}
